﻿namespace OpenIddictService
{
    using Microsoft.AspNetCore.Authentication.Cookies;
    using Microsoft.IdentityModel.Tokens;
    using OpenIddict.Abstractions;
    using System.Security.Cryptography;
    using System.Security.Cryptography.X509Certificates;
    using System.Text;
    using static OpenIddict.Abstractions.OpenIddictConstants;
    using static OpenIddict.Server.OpenIddictServerEvents;

    /// <summary>
    /// Defines the <see cref="OpenIddictExtension" />
    /// </summary>
    public static class OpenIddictExtension
    {
        /// <summary>
        /// The ConfigureOpenIddictExtension
        /// </summary>
        /// <param name="services">The services<see cref="IServiceCollection"/></param>
        public static void ConfigureOpenIddictExtension(this IServiceCollection services)
        {
            services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
             .AddCookie(options =>
             {
                 // 自定义登录地址
                 options.LoginPath = "/Account/Login"; // 这里设置你的登录页面路径
                 // 可选：还可以设置其他相关路径
                 options.LogoutPath = "/Account/Logout";
                 options.AccessDeniedPath = "/Account/AccessDenied";
             });

            services.AddOpenIddict()
                .AddServer(options =>
                {
                    // Enable the authorization, logout, token and userinfo endpoints.
                    options.SetAuthorizationEndpointUris("connect/authorize")
                     .SetEndSessionEndpointUris("connect/logout")
                     .SetTokenEndpointUris("connect/token")
                    .SetUserInfoEndpointUris("connect/userinfo");

                    options.RegisterScopes(Scopes.Profile, Scopes.OpenId);

                    options.AllowAuthorizationCodeFlow();

                    string baseDirectory = AppContext.BaseDirectory;
                    string path1 = Path.Combine(baseDirectory, "cert", "encryption-certificate.pfx");
                    string path2 = Path.Combine(baseDirectory, "cert", "signing-certificate.pfx");
                    var c1 = new X509Certificate2(path1);
                    var c2 = new X509Certificate2(path2);

                    options.AddEncryptionCertificate(c1);
                    options.AddSigningCertificate(c2);

                    options.UseAspNetCore()
                     .EnableAuthorizationEndpointPassthrough()
                     .EnableTokenEndpointPassthrough()
                     .EnableEndSessionEndpointPassthrough()
                     .EnableStatusCodePagesIntegration();

                    options.AddEventHandler<ValidateAuthorizationRequestContext>(builder =>
                   builder.UseInlineHandler(context =>
                   {
                       return default;
                   }));

                    options.AddEventHandler<ValidateTokenRequestContext>(builder =>
                        builder.UseInlineHandler(async context =>
                        {
                            // 只处理授权码授予类型
                            if (context.Request.IsAuthorizationCodeGrantType())
                            {
                                var clientId = context.Request.GetParameter(Parameters.ClientId)?.ToString();
                                var clientSecret = context.Request.GetParameter(Parameters.ClientSecret)?.ToString();

                                if (!string.Equals(clientId, "201902260001", StringComparison.Ordinal))
                                {
                                    context.Reject(
                                            error: Errors.InvalidGrant,
                                            description: "非法请求");
                                }
                                if (!string.Equals(clientSecret, "6C5327B841F24B52BCFD1292D6A91F57", StringComparison.Ordinal))
                                {
                                    context.Reject(
                                            error: Errors.InvalidGrant,
                                            description: "非法请求");
                                }

                                if (context.AuthorizationCodePrincipal == null)
                                {
                                    context.Reject(
                                         error: Errors.InvalidGrant,
                                         description: "非法请求");
                                }

                                var codeChallenge = context.AuthorizationCodePrincipal.GetClaim(Claims.Private.CodeChallenge);
                                var codeChallengeMethod = context.AuthorizationCodePrincipal.GetClaim(Claims.Private.CodeChallengeMethod);

                                if (string.IsNullOrEmpty(codeChallenge) || string.IsNullOrEmpty(codeChallengeMethod))
                                {
                                    context.Reject(
                                         error: Errors.InvalidGrant,
                                         description: "非法请求！");
                                }

                                if (string.Equals(codeChallengeMethod, CodeChallengeMethods.Sha256, StringComparison.Ordinal))
                                {
                                    string sign;
                                    using (var sha256 = SHA256.Create())
                                    {
                                        //校验
                                        sign = Base64UrlEncoder.Encode(sha256.ComputeHash(Encoding.ASCII.GetBytes(context.Request.CodeVerifier)));
                                    }
                                    if (!string.Equals(codeChallenge, sign, StringComparison.Ordinal))
                                    {
                                        context.Reject(error: Errors.InvalidGrant, description: "非法请求！");
                                    }
                                }

                            }

                            return;

                        }));

                    options.AddEventHandler<ValidateEndSessionRequestContext>(builder =>
                        builder.UseInlineHandler(context =>
                        {
                            return default;
                        }));

                    options.AddEventHandler<HandleEndSessionRequestContext>(builder =>
                                         builder.UseInlineHandler(context =>
                                         {
                                             return default;
                                         }));

                    options.AddEventHandler<HandleAuthorizationRequestContext>(builder =>
                          builder.UseInlineHandler(context =>
                          {
                              return default;
                          }));

                    options.EnableDegradedMode();
                })
        .AddValidation(options =>
        {
            // Import the configuration from the local OpenIddict server instance.
            options.UseLocalServer();

            // Register the ASP.NET Core host.
            options.UseAspNetCore();
        });
        }
    }
}
